<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue 2048 游戏</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <style>
        .game-container {
            background: #bbada0;
            border-radius: 10px;
            padding: 15px;
            position: relative;
        }
        
        .grid-row {
            display: flex;
            margin-bottom: 15px;
        }
        
        .grid-row:last-child {
            margin-bottom: 0;
        }
        
        .grid-cell {
            width: 80px;
            height: 80px;
            background: rgba(238, 228, 218, 0.35);
            border-radius: 6px;
            margin-right: 15px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            font-weight: bold;
            position: relative;
        }
        
        .grid-cell:last-child {
            margin-right: 0;
        }
        
        .tile {
            position: absolute;
            width: 80px;
            height: 80px;
            background: #eee4da;
            border-radius: 6px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            font-weight: bold;
            transition: all 0.15s ease-in-out;
            z-index: 10;
        }
        
        .tile-2 { background: #eee4da; color: #776e65; }
        .tile-4 { background: #ede0c8; color: #776e65; }
        .tile-8 { background: #f2b179; color: #f9f6f2; }
        .tile-16 { background: #f59563; color: #f9f6f2; }
        .tile-32 { background: #f67c5f; color: #f9f6f2; }
        .tile-64 { background: #f65e3b; color: #f9f6f2; }
        .tile-128 { background: #edcf72; color: #f9f6f2; font-size: 20px; }
        .tile-256 { background: #edcc61; color: #f9f6f2; font-size: 20px; }
        .tile-512 { background: #edc850; color: #f9f6f2; font-size: 20px; }
        .tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 18px; }
        .tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 18px; }
        
        .tile-new {
            animation: appear 0.2s ease-in-out;
        }
        
        @keyframes appear {
            0% {
                opacity: 0;
                transform: scale(0);
            }
            100% {
                opacity: 1;
                transform: scale(1);
            }
        }
        
        .tile-merged {
            animation: pop 0.2s ease-in-out;
        }
        
        @keyframes pop {
            0% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.2);
            }
            100% {
                transform: scale(1);
            }
        }
        
        .score-container {
            background: #bbada0;
            padding: 10px 20px;
            border-radius: 6px;
            color: white;
            font-weight: bold;
        }
        
        .rules-panel {
            transition: all 0.3s ease;
            max-height: 0;
            overflow: hidden;
        }
        
        .rules-panel.open {
            max-height: 500px;
        }
        
        .game-over {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(238, 228, 218, 0.73);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border-radius: 10px;
            z-index: 100;
        }
        
        .game-won {
            background: rgba(237, 194, 46, 0.5);
        }
    </style>
</head>
<body class="bg-gray-100 min-h-screen py-8">
    <div id="app">
        <div class="max-w-md mx-auto">
            <div class="flex justify-between items-center mb-4">
                <h1 class="text-4xl font-bold text-gray-800">2048</h1>
                <div class="flex gap-4">
                    <div class="score-container">
                        <div class="text-sm">得分</div>
                        <div class="text-xl">{{ score }}</div>
                    </div>
                    <div class="score-container">
                        <div class="text-sm">最高分</div>
                        <div class="text-xl">{{ bestScore }}</div>
                    </div>
                </div>
            </div>
            
            <div class="mb-4">
                <button @click="toggleRules" class="text-blue-600 hover:text-blue-800 flex items-center gap-2">
                    <i class="fas" :class="showRules ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
                    游戏规则
                </button>
                <div class="rules-panel bg-white p-4 rounded-lg shadow mt-2" :class="{ open: showRules }">
                    <h3 class="font-bold mb-2">游戏规则</h3>
                    <ul class="text-sm text-gray-600 space-y-1">
                        <li>• 使用方向键（↑↓←→）或 WASD 移动方块</li>
                        <li>• 当两个相同数字的方块碰撞时会合并成一个</li>
                        <li>• 每次移动后会随机生成一个新的 2 或 4</li>
                        <li>• 目标是创造出 2048 这个数字</li>
                        <li>• 当棋盘填满且无法移动时游戏结束</li>
                    </ul>
                </div>
            </div>
            
            <div class="game-container relative">
                <div v-for="(row, rowIndex) in grid" :key="rowIndex" class="grid-row">
                    <div v-for="(cell, colIndex) in row" :key="colIndex" class="grid-cell">
                        <div v-if="cell !== 0" 
                             class="tile tile-new"
                             :class="'tile-' + cell">
                            {{ cell }}
                        </div>
                    </div>
                </div>
                
                <div v-if="gameOver || gameWon" class="game-over" :class="{ 'game-won': gameWon }">
                    <h2 class="text-3xl font-bold mb-4">{{ gameWon ? '恭喜获胜！' : '游戏结束' }}</h2>
                    <p class="text-lg mb-4">最终得分: {{ score }}</p>
                    <button @click="newGame" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded">
                        重新开始
                    </button>
                </div>
            </div>
            
            <div class="mt-4 text-center">
                <button @click="newGame" class="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded">
                    新游戏
                </button>
            </div>
        </div>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.4.21/vue.global.min.js"></script>
    <script>
        const { createApp } = Vue;

        createApp({
            data() {
                return {
                    grid: [],
                    score: 0,
                    bestScore: 0,
                    gameOver: false,
                    gameWon: false,
                    showRules: false
                }
            },
            mounted() {
                this.initGame();
                this.loadBestScore();
                document.addEventListener('keydown', this.handleKeyPress);
            },
            beforeUnmount() {
                document.removeEventListener('keydown', this.handleKeyPress);
            },
            methods: {
                initGame() {
                    this.grid = Array(4).fill().map(() => Array(4).fill(0));
                    this.score = 0;
                    this.gameOver = false;
                    this.gameWon = false;
                    this.addNewTile();
                    this.addNewTile();
                },
                addNewTile() {
                    const emptyCells = [];
                    for (let i = 0; i < 4; i++) {
                        for (let j = 0; j < 4; j++) {
                            if (this.grid[i][j] === 0) {
                                emptyCells.push({ row: i, col: j });
                            }
                        }
                    }
                    
                    if (emptyCells.length > 0) {
                        const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                        this.grid[randomCell.row][randomCell.col] = Math.random() < 0.9 ? 2 : 4;
                    }
                },
                handleKeyPress(event) {
                    if (this.gameOver || this.gameWon) return;
                    
                    let moved = false;
                    const key = event.key.toLowerCase();
                    
                    if (['arrowup', 'w'].includes(key)) {
                        moved = this.moveUp();
                    } else if (['arrowdown', 's'].includes(key)) {
                        moved = this.moveDown();
                    } else if (['arrowleft', 'a'].includes(key)) {
                        moved = this.moveLeft();
                    } else if (['arrowright', 'd'].includes(key)) {
                        moved = this.moveRight();
                    }
                    
                    if (moved) {
                        this.$nextTick(() => {
                            this.addNewTile();
                            this.checkGameOver();
                            this.checkWin();
                        });
                    }
                },
                moveLeft() {
                    let moved = false;
                    const newGrid = this.grid.map(row => [...row]);
                    
                    for (let i = 0; i < 4; i++) {
                        const row = newGrid[i].filter(val => val !== 0);
                        const merged = [];
                        
                        for (let j = 0; j < row.length; j++) {
                            if (j < row.length - 1 && row[j] === row[j + 1]) {
                                merged.push(row[j] * 2);
                                this.score += row[j] * 2;
                                j++;
                            } else {
                                merged.push(row[j]);
                            }
                        }
                        
                        while (merged.length < 4) {
                            merged.push(0);
                        }
                        
                        for (let j = 0; j < 4; j++) {
                            if (newGrid[i][j] !== merged[j]) {
                                moved = true;
                            }
                            newGrid[i][j] = merged[j];
                        }
                    }
                    
                    if (moved) {
                        this.grid = newGrid;
                    }
                    
                    return moved;
                },
                moveRight() {
                    let moved = false;
                    const newGrid = this.grid.map(row => [...row]);
                    
                    for (let i = 0; i < 4; i++) {
                        const row = newGrid[i].filter(val => val !== 0);
                        const merged = [];
                        
                        for (let j = row.length - 1; j >= 0; j--) {
                            if (j > 0 && row[j] === row[j - 1]) {
                                merged.unshift(row[j] * 2);
                                this.score += row[j] * 2;
                                j--;
                            } else {
                                merged.unshift(row[j]);
                            }
                        }
                        
                        while (merged.length < 4) {
                            merged.unshift(0);
                        }
                        
                        for (let j = 0; j < 4; j++) {
                            if (newGrid[i][j] !== merged[j]) {
                                moved = true;
                            }
                            newGrid[i][j] = merged[j];
                        }
                    }
                    
                    if (moved) {
                        this.grid = newGrid;
                    }
                    
                    return moved;
                },
                moveUp() {
                    let moved = false;
                    const newGrid = this.grid.map(row => [...row]);
                    
                    for (let j = 0; j < 4; j++) {
                        const column = [];
                        for (let i = 0; i < 4; i++) {
                            if (newGrid[i][j] !== 0) {
                                column.push(newGrid[i][j]);
                            }
                        }
                        
                        const merged = [];
                        for (let i = 0; i < column.length; i++) {
                            if (i < column.length - 1 && column[i] === column[i + 1]) {
                                merged.push(column[i] * 2);
                                this.score += column[i] * 2;
                                i++;
                            } else {
                                merged.push(column[i]);
                            }
                        }
                        
                        while (merged.length < 4) {
                            merged.push(0);
                        }
                        
                        for (let i = 0; i < 4; i++) {
                            if (newGrid[i][j] !== merged[i]) {
                                moved = true;
                            }
                            newGrid[i][j] = merged[i];
                        }
                    }
                    
                    if (moved) {
                        this.grid = newGrid;
                    }
                    
                    return moved;
                },
                moveDown() {
                    let moved = false;
                    const newGrid = this.grid.map(row => [...row]);
                    
                    for (let j = 0; j < 4; j++) {
                        const column = [];
                        for (let i = 3; i >= 0; i--) {
                            if (newGrid[i][j] !== 0) {
                                column.unshift(newGrid[i][j]);
                            }
                        }
                        
                        const merged = [];
                        for (let i = column.length - 1; i >= 0; i--) {
                            if (i > 0 && column[i] === column[i - 1]) {
                                merged.unshift(column[i] * 2);
                                this.score += column[i] * 2;
                                i--;
                            } else {
                                merged.unshift(column[i]);
                            }
                        }
                        
                        while (merged.length < 4) {
                            merged.unshift(0);
                        }
                        
                        for (let i = 0; i < 4; i++) {
                            if (newGrid[i][j] !== merged[i]) {
                                moved = true;
                            }
                            newGrid[i][j] = merged[i];
                        }
                    }
                    
                    if (moved) {
                        this.grid = newGrid;
                    }
                    
                    return moved;
                },
                checkGameOver() {
                    for (let i = 0; i < 4; i++) {
                        for (let j = 0; j < 4; j++) {
                            if (this.grid[i][j] === 0) return;
                            if (i < 3 && this.grid[i][j] === this.grid[i + 1][j]) return;
                            if (j < 3 && this.grid[i][j] === this.grid[i][j + 1]) return;
                        }
                    }
                    this.gameOver = true;
                    this.saveBestScore();
                },
                checkWin() {
                    for (let i = 0; i < 4; i++) {
                        for (let j = 0; j < 4; j++) {
                            if (this.grid[i][j] === 2048) {
                                this.gameWon = true;
                                this.saveBestScore();
                                return;
                            }
                        }
                    }
                },
                newGame() {
                    this.initGame();
                },
                toggleRules() {
                    this.showRules = !this.showRules;
                },
                saveBestScore() {
                    if (this.score > this.bestScore) {
                        this.bestScore = this.score;
                        localStorage.setItem('bestScore', this.bestScore);
                    }
                },
                loadBestScore() {
                    const saved = localStorage.getItem('bestScore');
                    if (saved) {
                        this.bestScore = parseInt(saved);
                    }
                }
            }
        }).mount('#app');
    </script>
</body>
</html>